package container.queue;

import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * https://blog.csdn.net/f641385712/article/details/83691365
 * BlockingQueue 对插入操作、移除操作、获取元素操作提供了四种不同的方法，用于不同的场景中使用：
 * 1、抛出异常；如果试图的操作无法立即执行，抛一个异常。
 * 2、返回特殊值（null 或 true/false，取决于具体的操作）；
 * 3、阻塞等待此操作，直到这个操作成功；
 * 4、阻塞等待此操作，直到成功或者超时指定时间。
 * <pre>
 *          抛出异常     特殊值     阻塞     超时
 * 插入       add       offer     put     offer
 * 插入      remove     poll     take     poll
 * 插入     element     peek     不可用    不可用
 * </pre>
 * 备注：peek 方法的语义是只读取不移除
 * <p>
 * 放入数据：
 * * offer(anObject)：表示如果可能的话，将 anObject 加到 BlockingQueue 里，即如果 BlockingQueue 可以容纳，则返回 true，否则返回 false.（本方法不阻塞当前执行方法的线程）
 * * offer(E o, long timeout, TimeUnit unit)：可以设定等待的时间，如果在指定的时间内，还不能往队列中加入 BlockingQueue，则返回失败。
 * * put(anObject)：把 anObject 加到 BlockingQueue 里，如果 BlockQueue 没有空间，则调用此方法的线程被阻断，直到 BlockingQueue 里面有空间再继续。
 * 获取数据：
 * * poll(time)：取走 BlockingQueue 里排在首位的对象，若不能立即取出，则可以等 time 参数规定的时间，取不到时返回 null。
 * * poll(long timeout, TimeUnit unit)：从 BlockingQueue 取出一个队首的对象，如果在指定时间内，队列一旦有数据可取，则立即返回队列中的数据。否则直到时间超时还没有数据可取，返回失败。
 * * take()：取走 BlockingQueue 里排在首位的对象，若 BlockingQueue 为空，阻断进入，等待状态直到 BlockingQueue 有新的数据被加入。
 * * drainTo()：一次性从 BlockingQueue 获取所有可用的数据对象（还可以指定获取数据的个数），通过该方法，可以提升获取数据效率，不需要多次分批加锁或释放锁（一般用于批量操作）。
 * <p>
 * add：增加一个元索，如果队列已满，则抛出异常
 * remove：移除并返回队列头部的元素，如果队列为空，则抛出异常
 * element：返回队列头部的元素，如果队列为空，则抛出异常
 * offer：添加一个元素并返回 true，如果队列已满，则返回 false
 * poll：移除并返问队列头部的元素，如果队列为空，则返回 null
 * peek：返回队列头部的元素，如果队列为空，则返回 null
 * put：添加一个元素，如果队列满，则阻塞
 * take：移除并返回队列头部的元素，如果队列为空，则阻塞
 * <p>
 * 对于 BlockingQueue，我们的关注点应该在 put(e) 和 take() 这两个方法，因为这两个方法是带阻塞的。
 * BlockingQueue 是设计用来实现生产者-消费者队列的，当然，也可以将它当做普通的 Collection 来用，它实现了 java.util.Collection 接口。
 * 例如可以用 remove(x) 来删除任意一个元素，但是，这类操作通常并不高效，所以尽量只在少数的场合使用。比如一条消息已经入队，但是需要做取消操作的时候。
 * BlockingQueue 的实现都是线程安全的，但是批量的集合操作如 addAll, containsAll, retainAll 和 removeAll 不一定是原子操作。
 * 如 addAll 有可能在添加了一些元素后中途抛出异常，此时 BlockingQueue 中已经添加了部分元素，这个是允许的，取决于具体的实现。
 * <p>
 * BlockingQueue 不光实现了一个完整队列所具有的基本功能，同时在多线程环境下，它还自动管理了多线程间的自动等待于唤醒功能，从而使得程序员可以
 * 忽略这些细节，关注更高级的功能。
 * <p>
 * 另外提一点：非阻塞队列
 * 在并发编程中，有时候需要使用线程安全的队列。如果要实现一个线程安全的队列有两种方式：一种是使用阻塞算法，另一种是使用非阻塞算法。
 * 非阻塞队列：
 * ConcurrentLinkedQueue 是一个基于链接节点的无界线程安全队列，它采用先进先出的规则对节点进行排序，当我们添加一个元素的时候，它会添加到队列的
 * 尾部；当我们获取一个元素时，它会返回队列头部的元素。
 * LinkedList 继承 AbstractSequentialList，实现了 List 接口和 Deque 接口，元素可以为 null。非线程安全。
 * LinkedList 内部使用的数据结构是一个双向链表。如果要经常执行插入和删除操作，可以考虑使用 LinkedList。使用迭代器遍历 LinkedList。
 * <p>
 * 在并发编程中，一般推荐使用阻塞队列，这样实现可以尽量地避免程序出现意外的错误。
 * 阻塞队列使用最经典的场景就是 socket 客户端数据的读取和解析，读取数据的线程不断将数据放入队列，然后解析线程不断从队列取数据解析。
 * 还有其他类似的场景，只要符合生产者-消费者模型的都可以使用阻塞队列。
 * <p>
 * 使用非阻塞队列，虽然能即时返回结果（消费结果），但必须自行编码解决返回为空的情况处理（以及消费重试等问题）。
 */
public class T01_Readme {
    /**
     * <pre>
     * 1：对于 map/set 的选择使用
     * HashMap
     * TreeMap
     * LinkedHashMap
     *
     * Hashtable
     * Collections.sychronizedXXX
     *
     * ConcurrentHashMap
     * ConcurrentSkipListMap
     *
     * 2：队列
     * ArrayList
     * LinkedList
     * Collections.synchronizedXXX
     * CopyOnWriteList
     * Queue
     * CocurrentLinkedQueue / concurrentArrayQueue
     * BlockingQueue
     * 		TransferQueue
     * 		ArrayBlockingQueue 底层是数组，有界队列，如果我们要使用生产者-消费者模式，这是非常好的选择。
     *      LinkedBlockingQueue 底层是链表，可以当做无界和有界队列来使用，所以大家不要以为它就是无界队列。
     *      DelayQueue 是一个无界阻塞队列，只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的Delayed 元素。
     *      SynchronousQueue 本身不带有空间来存储任何元素，使用上可以选择公平模式和非公平模式。
     *      PriorityBlockingQueue 是无界队列，基于数组，数据结构为二叉堆，数组第一个也是树的根节点总是最小值。
     * </pre>
     */
    public static void main(String[] args) {
        Queue<Integer> q = new ArrayBlockingQueue<>(2);
        q.add(0);
        q.add(1);
        q.add(2);
        q.add(3);
        System.out.println(q);
    }
}
